Skip to main content

FNP - Advanced - Delete Operations & Visibility Control

Summary (Explain Like I’m 5)

Imagine a Google Doc where you delete text. The server stores:
  1. The deleted text (encrypted)
  2. A flag saying “deleted”
  3. A proof saying “I’m authorized to delete this”
Why keep deleted text? Because:
  • Multi-user: Other users might not have received the edit yet
  • Offline editing: User offline might insert text at position being deleted
  • Replication: Different regions might see delete at different times
  • Audit trail: History of what was deleted and by whom
FNP solves this using visibility flags + zero-knowledge proofs to prove you can delete without revealing who you are or what you deleted.

Technical Deep Dive

Delete Operation Protocol:
Delete Operation (Replica A):
  1. User clicks "delete" on character at position X
  2. Local state: mark character as invisible
  3. Generate operation:
     - char_id: [encrypted LSEQ position of target]
     - op_type: DELETE
     - lamport_clock: increment counter
     - replica_id: unique replica ID

  4. Generate Delete Proof (Halo2):
     - Witness (secret, only prover knows):
       - Target LSEQ identifier (plaintext)
       - Replica's M²-ORE secret key
       - Visibility permission (is replica authorized?)
       - Merkle path in document tree (membership proof)

     - Public Input (visible to server):
       - Encrypted target position
       - Merkle root of current document
       - Replica ID

     - Halo2 verifies (without decryption):
       ✓ Position is valid LSEQ identifier
       ✓ Merkle path proves membership in document
       ✓ Replica has authorization (signature check)
       ✓ No double-deletion (state machine)

  5. Sign operation with Dilithium
  6. Broadcast: {DELETE, encrypted_id, proof, signature}

Server - Merge Phase:
  1. Receive DELETE operation
  2. Verify Dilithium signature: ✓ Operation is authentic
  3. Verify Halo2 proof (blind): ✓ Deletion is authorized
  4. Verify Lamport clock: ✓ Causally consistent
  5. Lookup character by encrypted_id:
     - Find document_value where char_id = encrypted_id
  6. Mark as invisible:
     - document_value.visible = false
     - timestamp_deleted = now()
     - deleted_by = replica_id
  7. Broadcast: DELETE operation to other replicas

Other Replicas - Receive Phase:
  1. Receive DELETE operation
  2. Verify Halo2 proof (same as server)
  3. Local lookup by encrypted_id
  4. Mark as invisible
  5. Re-render document: invisible characters hidden from user
Visibility State Machine:
Character States:
  - VISIBLE: visible: true
    - Render to user
    - Can be deleted
    - Transition: VISIBLE → DELETED

  - DELETED: visible: false
    - Hidden from user
    - Can't be deleted again (idempotent)
    - Kept for causal consistency
    - Transition: DELETED → VISIBLE (undelete, future feature)

Deletion Idempotency:
  - Operation 1: DELETE char_id=100 (visible → deleted)
  - Operation 2: DELETE char_id=100 (already deleted, no-op)
  - Result: ✓ No double-deletion errors
Merkle Tree for Membership Proofs:
Document Represented as Merkle Tree:
  - Leaves: (char_id, content, visible, timestamp)
  - Hashes: SHA256(left_hash || right_hash)

Delete Proof Circuit:
  - Witness: Merkle path from leaf to root
  - Public: Root hash, char_id
  - Prove: "char_id is in this Merkle tree at root"
  - Path length: ~20 hashes for 1M characters
  - Proof constraints: ~50 per level × 20 levels = 1000 constraints

Advantage: Constant-size proofs regardless of document size!
  - 1,000 character document: proof ~400 bytes
  - 1,000,000 character document: proof ~400 bytes
  - Only Merkle path (20 hashes) differs, not full document
Concurrent Delete-Insert Conflicts:
Scenario: Two replicas simultaneously delete and insert same position

Timeline:
  t=0: Original doc: [A, B, C]

  t=1: Replica 1 → DELETE B
       Replica 2 → INSERT X between B and C

Case 1 - Replica 1 deletes first (receives operation first):
  t=1.5 (R1): receive DELETE B → mark B invisible
  t=2 (R1): receive INSERT X at [B.id, C.id]
  t=2.5 (R1): But B is invisible! Insert X after invisible B
  Result: [A, B(invisible), X, C]
  Render: [A, X, C] ✓

Case 2 - Replica 2 inserts first:
  t=1 (R1): receive INSERT X at [B.id, C.id] → [A, B, X, C]
  t=1.5 (R1): receive DELETE B → mark B invisible
  Result: [A, B(invisible), X, C]
  Render: [A, X, C] ✓

Invariant: LSEQ ordering + visibility flags ensure consistent view
regardless of operation arrival order (CRDT commutativity)
Undelete Capability (Future Enhancement):
// Undelete: restore character without re-insertion
fn propose_undelete(char_id: EncryptedLSEQIdentifier) -> Operation {
    Operation {
        op_type: UNDELETE,
        char_id,
        proof: generate_undelete_proof(),
        signature: sign_operation(),
    }
}

// Server-side:
// 1. Verify Halo2 proof (same Merkle membership check)
// 2. Verify character is marked DELETED (not already visible)
// 3. Set visible = true, timestamp_restored = now()
// 4. Broadcast to replicas

// Audit trail preserved:
// - Who deleted (Dilithium sig)
// - When deleted (timestamp_deleted)
// - Who undeleted (Dilithium sig)
// - When undeleted (timestamp_restored)

Mermaid Diagrams

Key Terms

  • Visibility Flag → Boolean marking character deleted (still stored for CRDT)
  • Merkle Proof → Cryptographic proof of membership in tree (constant size)
  • Idempotent Operation → Delete same character twice = single delete (no error)
  • Causal Consistency → Operation ordering respects happens-before relation
  • Undelete → Future feature; restore character with audit trail
  • Audit Trail → History of all modifications (who, when, what action)
  • Orphaned Character → Visible character whose parent is deleted
  • Deletion Timestamp → When character marked invisible; separate from creation

Q/A

Q: Why not permanently delete characters from server storage? A: CRDT semantics require all operations to be commutative. If offline user inserts text at deleted position, they don’t know position was deleted. Keeping deleted chars (marked invisible) ensures consistent merge. Also: audit trail, recovery, regulatory compliance. Q: How does deletion work if multiple users are editing same text? A: LSEQ gives each character unique ID. Delete operation targets specific ID (not position). Even if other characters inserted/deleted around it, target character ID remains constant. Delete finds and hides that specific ID. Q: Can I recover deleted text? A: Server stores encrypted deleted text (marked invisible). Deleted text remains encrypted. Only user who encrypted it (has Kyber_sk) can decrypt historical version. Recovery is manual (export encrypted document history). Permanent deletion requires cryptographic destruction. Q: What prevents user from forging a delete proof? A: Halo2 circuit verifies: (1) Merkle path is valid in current document, (2) Dilithium signature proves authorization. Forging proof requires breaking lattice hardness (LWE) or discrete log (DSA). Cryptographically infeasible. Q: What if server crash loses visibility flags? A: PostgreSQL persistence layer stores operation log. On restart, replay operations in Lamport clock order. All deletes replayed → visibility flags restored from log. No data loss. Q: How many concurrent deletions can system handle? A: As many as proofs can be verified (~1000s ops/sec). Each DELETE generates independent proof. No global lock needed. Parallelizable across server replicas.

Example / Analogy

Shared Notebook Analogy: Imagine a physical notebook passed between 3 friends: Without proper deletion (naive approach):
  • Friend 1 writes “Hello”
  • Friend 2 erases word with permanent marker
  • Friend 3 (offline, working on copy) doesn’t know about erasure
  • Friend 3 inserts “Goodbye” at erased position
  • Now both friends have different notebooks ✗
With FNP deletion (marks invisible):
  • Friend 1 writes “Hello” at position [A, B, C]
  • Friend 2 says “I want to hide [A, B, C]” (with proof)
  • Friend 2 crosses out [A, B, C] but leaves pencil marks (visible = false)
  • Friend 3 (offline) inserts “Goodbye” between [A, B, C] and [D, E]
  • Both friends compare: [A, B, C (crossed), Goodbye, D, E]
  • When Friend 3 learns [A, B, C] is crossed: both show [Goodbye, D, E] ✓

Cross-References: FNP Protocol Flow, LSEQ CRDT, Halo2 Circuits, CRDT Semantics Category: Protocol | CRDT | Data Management | Advanced Difficulty: Advanced ⭐⭐⭐⭐ Updated: 2025-11-28